//#define CXXOMFORT_NOTICES 2
#include <cxxomfort/cxxo-utils.hpp>
#include <cxxomfort/backports.hpp>
#include <cxxomfort/typeindex.hpp>
#include <cxxomfort/library/type_name.hpp>
#include <cstddef>
#include <iostream>
#include <vector>
#include <list>

typedef cxxomfort::typelist<unsigned int, std::string, float, bool * >
TypeList;

enum Eenum { E1= 1, E2= 2, E3= 3};

void test_ops();
void test_compose();
void test_derive();
void test_satisfy();

int main () {
	using namespace std;
	using cxxomfort::library::type_name;
	using cxxomfort::library::typeid_demangle;
	cxxomfort::output_info(stdout); cout<< endl;
	cout<< "TL "<< cxxomfort::type_name<TypeList>()<< " sizeof "<< sizeof(TypeList)<< endl;
	cout<< "size        : "<< cxxomfort::typelist_size<TypeList>::value<< endl;
	cout<< "find float  : "<< cxxomfort::typelist_find<float, TypeList>::value<< endl;
	cout<< "has float   : "<< (cxxomfort::typelist_find<float, TypeList>::value < cxxomfort::typelist_size<TypeList>::value) << endl;
	cout<< "find double : "<< cxxomfort::typelist_find<double, TypeList>::value<< endl;
	cout<< "has double  : "<< (cxxomfort::typelist_find<double, TypeList>::value < cxxomfort::typelist_size<TypeList>::value) << endl;
	
	cout<< "check for fundamental"<< endl;
	cout<< cxxomfort::typelist_count_if< std::is_fundamental, TypeList >::value << endl;
	cout<< "check for pointer"<< endl;
	cout<< cxxomfort::typelist_count_if< std::is_pointer, TypeList >::value << endl;

#if 0
	// the next line should fail to compile
	typedef typename
		cxxomfort::typelist_get< cxxomfort::typelist_size<TypeList>::value , TypeList>::type
		Wrong;
	cout<< cxxomfort::type_name<Wrong>()<< endl;
#endif
	
	test_ops();

	test_satisfy(); 
	test_compose();

	test_derive();

}

void test_ops () {
	using namespace std;
	using namespace cxxomfort;
	cout<< "--\nPush/Pop test:"<< endl;
	typedef typename typelist_push< int, TypeList >::type TL2;
	typedef typename typelist_pop< TL2 >::type TL3;
	static_assert( (std::is_same<TypeList,TL3>::value), "push-pop on typelist failed!");
	cout<< "TL2 "<< type_name<TL2>()<< endl;
	cout<< "TL3 "<< type_name<TL3>()<< endl;
	#if (CXXOMFORT_CXX_STD>=2011)
	typedef typename typelist_cut<1, TypeList>::type TC1;
	typedef typename typelist_cut<2, TypeList>::type TC2;
	cout<< "TC1 "<< type_name<TC1>()<< endl;
	cout<< "TC2 "<< type_name<TC2>()<< endl;
	#endif
	
}

void test_satisfy () {
	using namespace std;
	using namespace cxxomfort;
	cout<< "--\nSatisfy test:"<< endl;
	#if (1 || CXXOMFORT_CXX_STD >= 2011)
	typedef typelist< int, bool, unsigned long, float > TNumerics;
	typedef typelist< int, bool, char*, size_t, long double, nullptr_t > TNonNumerics;
	cout<< "TL Numerics "<< cxxomfort::type_name<TNumerics>()<< endl;
	cout<< "integers? \t"<< (typelist_all_if<TNumerics, std::is_integral>::value)<< endl;
	cout<< "arithmetic? \t"<< (typelist_all_if<TNumerics, std::is_arithmetic>::value)<< endl;
	cout<< "TL NonNumerics "<< cxxomfort::type_name<TNonNumerics>()<< endl;
	cout<< "integers? \t"<< (typelist_all_if<TNonNumerics, std::is_integral>::value)<< endl;
	cout<< "arithmetic? \t"<< (typelist_all_if<TNonNumerics, std::is_arithmetic>::value)<< endl;
	#else
	cout<< "Not implemented"<< endl;
	#endif
	
}

void test_compose () {
	using namespace std;
	using namespace cxxomfort;
	cout<< "--\nWrap/Compose test:"<< endl;

	typedef typelist<std::pair<Eenum, std::string_view>, std::string, cxxomfort::Exactly<float>, bool* > TL1;
	cout<< "TL1 "<< cxxomfort::type_name<TL1>()<< endl;
	typedef typelist_wrap< std::reference_wrapper , TL1>::type TW1;
	cout<< "TW1 "<< cxxomfort::type_name<TW1>()<< endl;
	typedef typelist_compose< std::decay , TL1>::type TC1;
	cout<< "TC1 "<< cxxomfort::type_name<TC1>()<< endl;

}

// derived test
// types need to be nonlocal for C++<11

typedef cxxomfort::typelist< std::string, std::wstring > TL1;
typedef cxxomfort::typelist_derive< TL1 >::type Deriveds;
struct TL_Multistring: Deriveds {};
void test_derive () {
	using namespace std;
	using namespace cxxomfort;
	cout<< "--\nDerive test:"<< endl;
	#if (1 || CXXOMFORT_CXX_STD>=2011)
	cout<< "TL1 "<< cxxomfort::type_name<TL1>()<< endl;
	cout<< "Deriveds: "<< cxxomfort::type_name<Deriveds>()<< endl;
	cout<< "TL2 "<< cxxomfort::type_name<TL_Multistring>()<< endl;
	static_assert((is_base_of<std::wstring, TL_Multistring>::value), "Base");
	#else
	cout<< "Not implemented"<< endl;
	#endif

}

